using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Web;

namespace cb0tServer
{
    class WSHelpers
    {
        private static IPAddress last_guest = IPAddress.Parse("0.0.0.0");

        public static bool ProcessReceivedLine(WebClientObject c, byte[] buffer)
        {
            String line = Encoding.Default.GetString(buffer);

            if (line.StartsWith("GET "))
            {
                String[] str_split = line.Substring(4).Split(new String[] { " " }, StringSplitOptions.None);

                if (str_split.Length <= 1) return false;

                c.RequestedUrl = str_split[0]; // got requested filename
                c.ProtocolVersion = str_split[1]; // got protocol version
                return false;
            }

            if (line.StartsWith("POST "))
            {
                String[] str_split = line.Substring(5).Split(new String[] { " " }, StringSplitOptions.None);

                if (str_split.Length <= 1) return false;

                c.RequestedUrl = str_split[0]; // got requested filename
                c.ProtocolVersion = str_split[1]; // got protocol version
                return false;
            }

            if (line.StartsWith("User-Agent: "))
            {
                c.UserAgent = line.Substring(12); // got user agent
                return false;
            }

            if (line.StartsWith("Accept: "))
            {
                c.AcceptedContentType = line.Substring(8).Split(new String[] { "," },
                    StringSplitOptions.RemoveEmptyEntries); // got accepted content types
                return false;
            }

            if (String.IsNullOrEmpty(line))
            {
                return true; // client is done sending request info
            }

            return false; // could use POST info in the future
        }

        public static bool PopulateOutboundPackets(WebClientObject c)
        {
            if (c.RequestedUrl.StartsWith("/"))
                c.RequestedUrl = c.RequestedUrl.Substring(1);

            if (c.RequestedUrl.IndexOf("?") > -1)
                c.RequestedUrl = c.RequestedUrl.Split(new String[] { "?" }, StringSplitOptions.None)[0]; // could use GET info in the future

            c.RequestedUrl = c.RequestedUrl.Trim(); // filename tidied up

            if (String.IsNullOrEmpty(c.RequestedUrl))
                c.RequestedUrl = "index.sb0t"; // no specific file requested therefore send index page

            c.ContentType = BuildMIME(c.RequestedUrl); // set Content-Type

            if (File.Exists(sb0t_winGUI.RunOnceFileUpdater.sbotpath + "website/" + c.RequestedUrl))
            {
                try
                {
                    byte[] file_data = File.ReadAllBytes(sb0t_winGUI.RunOnceFileUpdater.sbotpath + "website/" + c.RequestedUrl);

                    if (c.RequestedUrl.EndsWith(".sb0t"))
                        file_data = ParseSb0tVariables(file_data, c.ip);

                    c.file_length = file_data.Length; // content length
                    c.AddPacket(BuildHeader(c)); // queue response header
                    long chunks = (long)Math.Floor((double)c.file_length / 2048); // max chunk size 2048 bytes
                    List<byte> file_bytes = new List<byte>(file_data);
                    file_data = null;

                    for (long i = 0; i < chunks; i++) // queue up 2048 byte packet chunks
                    {
                        c.AddPacket(file_bytes.GetRange(0, 2048).ToArray());
                        file_bytes.RemoveRange(0, 2048);
                    }

                    if (file_bytes.Count > 0) // queue up remainder
                    {
                        c.AddPacket(file_bytes.ToArray());
                    }

                    return true;
                }
                catch { }
            }

            return false;
        }

        private static byte[] BuildHeader(WebClientObject c)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine(c.ProtocolVersion + " 200 OK");
            sb.AppendLine("Date: " + BuildTimestamp(DateTime.Now.ToUniversalTime()));
            sb.AppendLine("Server: sb0t webserver application by oobe");
            sb.AppendLine("Last-Modified: " + BuildTimestamp(DateTime.Now.ToUniversalTime()));
            sb.AppendLine("Connection: close");
            sb.AppendLine("Accept-Ranges: bytes");
            sb.AppendLine("Content-Length: " + c.file_length);
            sb.AppendLine("Content-Type: " + c.ContentType);
            sb.AppendLine("");

            return Encoding.Default.GetBytes(sb.ToString());
        }

        private static String BuildTimestamp(DateTime d)
        {
            String[] months = new String[]
            {
                "Jan", "Feb", "Mar", "Apr", "May", "Jun",
                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
            };

            StringBuilder sb = new StringBuilder();
            sb.Append(d.DayOfWeek.ToString().Substring(0, 3) + ", ");
            sb.Append(d.Day + " ");
            sb.Append(months[d.Month] + " ");
            sb.Append(d.Year + " ");
            sb.Append((d.Hour > 10 ? d.Hour.ToString() : ("0" + d.Hour)) + ":");
            sb.Append((d.Minute > 10 ? d.Minute.ToString() : ("0" + d.Minute)) + ":");
            sb.Append((d.Second > 10 ? d.Second.ToString() : ("0" + d.Second)) + " GMT");

            return sb.ToString();
        }

        private static String BuildMIME(String path)
        {
            if (path.EndsWith(".gif"))
                return "image/gif";

            if (path.EndsWith(".jpg"))
                return "image/jpg";

            if (path.EndsWith(".bmp"))
                return "image/bmp";

            if (path.EndsWith(".png"))
                return "image/png";

            if (path.EndsWith(".html"))
                return "text/html; charset=utf-8";

            if (path.EndsWith(".htm"))
                return "text/html; charset=utf-8";

            if (path.EndsWith(".sb0t"))
                return "text/html; charset=utf-8";

            if (path.EndsWith(".txt"))
                return "text/plain; charset=utf-8";

            if (path.EndsWith(".css"))
                return "text/css";

            if (path.EndsWith(".js"))
                return "text/javascript; charset=utf-8";

            return "text/plain; charset=utf-8";
        }

        private static byte[] ParseSb0tVariables(byte[] buf, IPAddress client_addr) // add sbot info later
        {
            if (!last_guest.Equals(client_addr))
            {
                last_guest = client_addr;
                WebServerSettings.hits++;
                WebServerSettings.Save();
            }

            List<String> host_list = new List<String>();
            int admin_count = 0;

            foreach (UserObject ri in UserPool.Users)
            {
                if (ri.IsSet())
                {
                    if (ri.logged_in)
                    {
                        if (ri.host) host_list.Add(ri.name);
                        if (ri.admin) admin_count++;
                    }
                }
            }

            UserObject clientobj = new UserObject();
            clientobj.physical_ip = client_addr;
            WhowasUserItem last = Whowas.CheckLastSeen(clientobj);
            String hash = CoreSettings.BuildHashlinkForWebsite();
            String sb0t_str = Encoding.UTF8.GetString(buf);
            QuoteItem quoteobj = Quotes.CallRandomQuote();
            String quote = quoteobj == null ? "no quotes have been added" : quoteobj.text;

            sb0t_str = sb0t_str.Replace("%ip", client_addr.ToString());
            sb0t_str = sb0t_str.Replace("%roomname", HtmlEncode(CoreSettings.room_name));
            sb0t_str = sb0t_str.Replace("%hashlink", (hash == null ? "" : hash));
            sb0t_str = sb0t_str.Replace("%uptime", Stats.GetUptime());
            sb0t_str = sb0t_str.Replace("%usercount", UserPool.UserCount().ToString());
            sb0t_str = sb0t_str.Replace("%admincount", admin_count.ToString());
            sb0t_str = sb0t_str.Replace("%roomlocation", HtmlEncode(CoreSettings.room_location));
            sb0t_str = sb0t_str.Replace("%hoststatus", HtmlEncode(Settings.host_status));
            sb0t_str = sb0t_str.Replace("%hitcounter", WebServerSettings.hits.ToString());
            sb0t_str = sb0t_str.Replace("%randomquote", HtmlEncode(quote));
            sb0t_str = sb0t_str.Replace("%hostnames", host_list.Count > 0 ? HtmlEncode(String.Join(", ", host_list.ToArray())) : "None");
            sb0t_str = sb0t_str.Replace("%lastseen", (last == null ? "" : HtmlEncode(last.name)));
            sb0t_str = sb0t_str.Replace("%userlist", BuildUserList());

            return Encoding.UTF8.GetBytes(sb0t_str);
        }

        private static String HtmlEncode(String text)
        {
            char[] chars = HttpUtility.HtmlEncode(text).ToCharArray();
            StringBuilder result = new StringBuilder(text.Length + (int)(text.Length * 0.1));

            foreach (char c in chars)
            {
                int value = Convert.ToInt32(c);

                if (value > 127)
                    result.AppendFormat("&#{0};", value);
                else
                    result.Append(c);
            }

            return result.ToString();
        }

        private static String BuildUserList()
        {
            List<String> names = new List<String>();

            foreach (UserObject u in UserPool.Users)
                if (u.IsSet())
                    if (u.logged_in)
                        names.Add(u.name);

            if (names.Count == 0) return String.Empty;

            StringBuilder builder = new StringBuilder();

            builder.AppendLine("<table class=\"userlist\">");

            foreach (String name in names)
                builder.AppendLine("  <tr><td class=\"userlist_item\">" + HtmlEncode(name) + "</td></tr>");

            builder.AppendLine("</table>");

            return builder.ToString();
        }
    }
}
